package com.zhdl.network.motor.analysis;

import cn.hutool.core.util.HexUtil;
import com.project.command.faq.analysis.ComAnalysis;
import com.project.command.faq.analysis.ComAnalysisData;
import com.project.command.faq.analysis.ComAnalysisResult;
import com.project.common.util.CRC16;
import com.zhdl.network.modbus.dto.ModbusDto;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service(MotorAnalysis.MOTOR_ANALYSIS)
public class MotorAnalysis implements ComAnalysis<ModbusDto, ByteBuf> {

    public static final String MOTOR_ANALYSIS = "modbus_east";

    ByteBuf buf = Unpooled.buffer();// 无限制长度


    /**
     * com原始返回数据,进行解析
     * 将ByteBuf 数据,转换为list初步解析对象
     * 接收示例数据 01 05 00 00 FF 00 8C 3A
     *
     * @param data
     * @return 可能多个数据包
     */
    @Override
    public ComAnalysisResult<ModbusDto> analysisData(ComAnalysisData<ByteBuf> data) {
        List<ModbusDto> fullFrames = new ArrayList<ModbusDto>();
        try {
            // 循环读取数据流中的数据，一个字节一个字节的读取
            // mark当前Index，如果无法处理，则重置index，再有数据时仍然从头开始匹配
            buf = data.getBuf();
            buf.markReaderIndex();
            int breakCnt = 0;
            while (buf.isReadable()) {
                buf.readBytes(breakCnt);   // 跳过上次循环读的n个字节
                breakCnt++;
                ModbusDto modbusDto = new ModbusDto();

                // 判断是否满足字节最小数目,不满足直接return等待数据到来
                if (buf.readableBytes() < 4) {
                    buf.resetReaderIndex();
                    return new ComAnalysisResult<>(fullFrames);
                }

                // 记录读取Index
                int readIndex = buf.readerIndex();

                // 读一个字节判断，一次只读一个字节，无法处理时读下一个字节，能正常解析时，读完整数据包的长度
                modbusDto.setAddress(buf.readByte()); // 地址
                byte cmdType = buf.readByte(); // 功能码
                modbusDto.setCmdType(cmdType);
                //if (cmdType == (byte) 0x05) {  //功能码
                if (cmdType == (byte) 0x03 || cmdType == (byte) 0x06 || cmdType == (byte) 0x10) {  //功能码 0x06,0x10：写数据寄存器返回 6-26
                    // (0x05) 写单个线圈寄存器 00 00 FF 00 8C 3A
                    // 00 00 写单个线圈寄存器地址 2HEX 高字节 低字节
                    // FF 00 写入数据，2HEX 继电器吸合：FF00 继电器断开：0000
                    if (cmdType == (byte) 0x03) //?  6-30 读取状态字，9437 表示原点找到  24 DD
                    {
                        byte[] addBuff = new byte[1];
                        buf.readBytes(addBuff);    // 数据区，长度是len
                    } else {
                        byte[] addBuff = new byte[2];
                        buf.readBytes(addBuff);    // 数据区，长度是len
                    }
                    byte[] unitBuff = new byte[2];
                    buf.readBytes(unitBuff);    // 数据区，长度是len
                    modbusDto.setUnitBuff(unitBuff);

                    byte[] crcBs = new byte[2];
                    buf.readBytes(crcBs);

                    ByteBuffer fullFrame = ByteBuffer.allocate(buf.readerIndex() - readIndex);
                    buf.getBytes(readIndex, fullFrame);
                    modbusDto.setFullBuff(fullFrame.array());


                    // crc校验
                    byte[] calcBytes = new byte[modbusDto.getFullBuff().length - 2];
                    System.arraycopy(modbusDto.getFullBuff(), 0, calcBytes, 0, calcBytes.length);
                    byte[] crc = CRC16.MODBUS(calcBytes);

                    /**
                     * 暂时注释掉签名验证部分信息
                     */
                    if (crc[0] != modbusDto.getFullBuff()[modbusDto.getFullBuff().length - 2] || crc[1] != modbusDto.getFullBuff()[modbusDto.getFullBuff().length - 1]) {
                        buf.resetReaderIndex();
                        continue;  // 签名不通过，不是有效数据包
                    }

                    /*
                        测试签名部分数据是否有效
                        log.info(HexUtil.encodeHexStr(calcBytes)+"----------------------0");
                        log.info(HexUtil.encodeHexStr(crc)+"----------------------1");
                     */

                    fullFrames.add(modbusDto);
                    breakCnt = 0;
                }
            }
        } catch (Exception ex) {
//            ex.printStackTrace();

            //从头开始读
            buf.resetReaderIndex();
            //打印错误数据信息
            byte[] bytes = new byte[buf.readableBytes()];
            buf.readBytes(bytes);
            log.info(HexUtil.encodeHexStr(bytes));
            //清除数据
            data.buf.clear();
            return new ComAnalysisResult<>(fullFrames);
        }
        return new ComAnalysisResult<>(fullFrames);
    }


    /**
     * 获取协议头信息
     *
     * @return
     */
    @Override
    public String getHeadAnalysis() {
        return "";
    }

    /**
     * 协议追加校验位信息（尾部数据）
     *
     * @param cmdStr
     * @return
     */
    @Override
    public byte[] getAfterAnalysis(String cmdStr) {
        return HexUtil.decodeHex(cmdStr);
    }
}
